package net.peelmeagrape.hibernate.ant;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.util.ClasspathUtils;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class HibernateMappingsGenerateTask
        extends MatchingTask
{
    private Path classpath;
    private File destDir;
    private File srcDir;

    /**
     * Set the classpath to be used when running the Java class
     *
     * @param s an Ant Path object containing the classpath.
     */
    public void setClasspath(Path s)
    {
        if (classpath == null)
            classpath = new Path(getProject());
        classpath.append(s);
    }

    public Path createClasspath()
    {
        if (classpath == null)
            classpath = new Path(getProject());
        return classpath.createPath();
    }

    public void setSrcDir(File srcDir)
    {
        this.srcDir = srcDir;
    }

    public File getDestDir()
    {
        return destDir;
    }

    public void setDestDir(File destDir)
    {
        this.destDir = destDir;
    }

    public void execute() throws BuildException
    {
        try
        {
            destDir.mkdirs();
            ClassLoader classLoaderForPath = ClasspathUtils.getClassLoaderForPath(getProject(), classpath, "hibernate-mappings", true);
            List<String> classNames = getAllClassNames(classLoaderForPath);

            ArrayList<String> args = new ArrayList<String>();
            args.add(destDir.getCanonicalPath());
            args.addAll(classNames);
            Class<?> genMappingsClass = classLoaderForPath.loadClass("net.peelmeagrape.hibernate.ant.GenerateMappings");
            Method method = genMappingsClass.getMethod("main", String[].class);
            method.invoke(null,new Object[] {args.toArray(new String[0])});
        }
        catch (IOException e)
        {
            throw new BuildException(e.getMessage(),e);
        }
        catch (ClassNotFoundException e)
        {
            throw new BuildException(e.getMessage(),e);
        }
        catch (NoSuchMethodException e)
        {
            throw new BuildException(e.getMessage(),e);
        }
        catch (IllegalAccessException e)
        {
            throw new BuildException(e.getMessage(),e);
        }
        catch (InvocationTargetException e)
        {
            throw new BuildException(e.getMessage(),e);
        }

    }

    private List<String> getAllClassNames(ClassLoader classLoaderForPath)
    {
        DirectoryScanner directoryScanner = getDirectoryScanner(srcDir);
        List<String> classNames = new ArrayList<String>();
        String[] includedFiles = directoryScanner.getIncludedFiles();
        for (String includedFile : includedFiles)
        {
            String classNameFromFileName = getClassNameFromFileName(includedFile);
            Class classToScan = getClassIfExists(classLoaderForPath, classNameFromFileName);
            classNames.add(classToScan.getName());
        }
        return classNames;
    }

    private Class getClassIfExists(ClassLoader classLoader, String classNameFromFileName)
    {
        try
        {
            return classLoader.loadClass(classNameFromFileName);
        }
        catch (ClassNotFoundException e)
        {
            log("Couldn't load class: " + classNameFromFileName + ": " + e.getMessage());
            return null;
        }
    }

    private String getClassNameFromFileName(String includedFile)
    {
        if (includedFile.indexOf(".") != -1)
            includedFile = includedFile.substring(0, includedFile.indexOf("."));
        return includedFile.replace('/', '.').replace('\\', '.');
    }
}
